﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QD.Data.Query
{
    public class QueryField : Query
    {
        private String name;
        public String Name
        {
            get
            {
                if (this.Table != null)
                    return String.Concat(this.Table.Location, ".", name, "");
                else
                    return name;
            }
            protected set { name = value; }
        }
        public String ShortName { get { return name; } }
        public Type DataType { get; protected set; }
        public QueryTable Table { get; protected set; }
        public Int32 FieldIndex { get; protected set; }
        #region Settings
        protected const String TRUE = "1";
        protected const String FALSE = "0";
        #endregion

        public QueryField(String name) : this() { this.name = name; }
        public QueryField() { base.Order = String.Empty; base.Expression = String.Empty; }

    }

    public class QueryField<T> : QueryField
    {


        public QueryField(QueryTable table, String Name, Int32 index = 0)
            : this(Name)
        {
            this.Table = table;
            this.FieldIndex = index;
        }
        public QueryField(String Name)
        {
            this.Name = Name;
            this.DataType = typeof(T);
        }

        public QueryField(QueryField<T> q)
        {
            this.Name = q.Name;
            base.Expression = Concat("(", q.Expression, ")");
        }
        private String GetExpression(T value)
        {
            String exp = null;
            if (DataType == typeof(String))
                exp = String.Concat("'", value.ToString().Replace("'", "''"), "'");
            else if (DataType == typeof(DateTime) || DataType == typeof(DateTime?))
                exp = String.Concat("'", Convert.ToDateTime(value).ToString("yyyy-MM-dd HH:mm:ss.fff"), "'");
            else if (DataType == typeof(Boolean))
                exp = Convert.ToBoolean(value) ? TRUE : FALSE;
            else
                exp = value.ToString();
            return exp;
        }

        #region methods
        public Query Equal(T value)
        {
            Query clone = this.Clone();
            if (value == null)
            {
                clone.Expression = this.Name + " IS NULL ";
                return clone;
            }
            clone.Expression = this.Name + " = " + GetExpression(value);

            return clone;
        }

        public Query NotEqual(T value)
        {
            Query clone = this.Clone();
            if (value == null)
                clone.Expression = String.Concat(this.Name, " IS NOT NULL ");
            else
                clone.Expression = String.Concat(this.Name, " <> ", this.GetExpression(value));
            return clone;
        }

        public Query Like(T value)
        {
            Query clone = this.Clone();
            clone.Expression = String.Concat(this.Name, " like '", value.ToString(), "'");
            return clone;
        }

        public Query In(T[] values)
        {
            Query clone = this.Clone();
            if (values != null && values.Length > 0)
            {
                clone.Expression = this.Name + " IN (";
                foreach (T item in values)
                {
                    clone.Expression += this.GetExpression(item) + ",";
                }
                if (clone.Expression.EndsWith(","))
                    clone.Expression = this.Expression.Substring(0, this.Expression.Length - 1) + ")";
            }
            return clone;
        }

        public Query In(String value)
        {
            Query clone = this.Clone();
            if (!String.IsNullOrEmpty(value))
                clone.Expression = String.Format("{0} IN({1})", this.Name, value);
            return clone;
        }

        public Query Between(T v1, T v2)
        {
            Query clone = this.Clone();
            clone.Expression = Concat(this.Name, "BETWEEN", GetExpression(v1), "AND", GetExpression(v2));
            return clone;
        }

        private Query LessThan(T value, Boolean equal)
        {
            Query clone = this.Clone();
            clone.Expression = Concat(this.Name, equal ? OperateExp.LessThen : OperateExp.Less, GetExpression(value));
            return clone;
        }

        private Query GreaterThan(T value, Boolean equal)
        {
            Query clone = this.Clone();
            clone.Expression = Concat(this.Name, equal ? OperateExp.GreatThen : OperateExp.Greater, GetExpression(value));
            return clone;
        }

        public Query HasBit(Int32 val)
        {
            Query clone = this.Clone();
            clone.Expression = Concat("(", this.Name, "&", val.ToString(), ") = ", val.ToString());
            return clone;
        }

        public Query HasBit(Int64 val)
        {
            Query clone = this.Clone();
            clone.Expression = Concat("(", this.Name, "&", val.ToString(), ") = ", val.ToString());
            return clone;
        }


        #endregion

        private Query Clone()
        {
            Query t = new QueryField<T>(this.Name);
            t.Expression = this.Expression;
            return t;
        }

        #region
        private String Concat(params String[] str)
        {
            StringBuilder text = new StringBuilder();
            foreach (String s in str)
                text.AppendFormat("{0} ", s);
            text.Remove(text.Length - 1, 1);
            return text.ToString();
        }

        public override Boolean Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion

        #region overload operator

        public static Query operator ==(QueryField<T> q, T value)
        {
            return q.Equal(value);
        }

        public static Query operator !=(QueryField<T> q, T value)
        {
            return q.NotEqual(value);
        }

        public static Query operator <=(QueryField<T> q, T value)
        {
            return q.LessThan(value, true);
        }

        public static Query operator >=(QueryField<T> q, T value)
        {
            return q.GreaterThan(value, true);
        }

        public static Query operator <(QueryField<T> q, T value)
        {
            return q.LessThan(value, false);
        }

        public static Query operator >(QueryField<T> q, T value)
        {
            return q.GreaterThan(value, false);
        }

        public static Query operator %(QueryField<T> q, T value)
        {
            return q.Like(value);
        }

        #endregion

    }
}
